#include "qe_log.h"
#include "qe_i2c.h"
#include "qe_assert.h"



QELOG_DOMAIN(QELOG_DOMAIN_IIC);



static qe_size i2c_bus_read(qe_dev *dev, qe_offs pos,
	void *buffer, qe_size count)
{
	qe_u16 addr;
	qe_u16 flags;
	qe_i2c_bus *bus = (qe_i2c_bus *)dev;

	qe_assert(dev != QE_NULL);
	qe_assert(buffer != QE_NULL);

	addr = pos & 0xffff;
	flags = (pos >> 16) & 0xffff;

	return qe_i2c_master_recv(bus, addr, flags, (qe_u8 *)buffer, count);
}

static qe_size i2c_bus_write(qe_dev *dev, qe_offs pos, const void *buffer, qe_size count)
{
	qe_u16 addr;
	qe_u16 flags;
	qe_i2c_bus *bus = (qe_i2c_bus *)dev;

	qe_assert(dev != QE_NULL);
	qe_assert(buffer != QE_NULL);

	qe_debug("%s write %d bytes", bus->parent.name, count);

	addr = pos & 0xffff;
	flags = (pos >> 16) & 0xffff;

	return qe_i2c_master_send(bus, addr, flags, (const qe_u8 *)buffer, count);
}

static qe_ret i2c_bus_ioctl(qe_dev *dev, int cmd, void *args)
{
	qe_ret ret;
	qe_i2c_priv_data *priv_data;
	qe_i2c_bus *bus = (qe_i2c_bus *)dev;

	qe_assert(bus != QE_NULL);

	switch (cmd) {

	case QE_I2C_DEV_CTRL_10BIT:
		bus->flags |= QE_I2C_ADDR_10BIT;
		break;

	case QE_I2C_DEV_CTRL_ADDR:
		bus->addr = *(qe_u16 *)args;
		break;

	case QE_I2C_DEV_CTRL_TIMEOUT:
		bus->timeout = *(qe_u32 *)args;
		break;

	case QE_I2C_DEV_CTRL_RW:
		priv_data = (qe_i2c_priv_data *)args;
		ret = qe_i2c_transfer(bus, priv_data->msgs, priv_data->number);
		if (ret < 0) {
			return qe_err_send;
		}
		break;

	case QE_I2C_DEV_CTRL_CLK:
		break;

	default:
		break;
	}

	return qe_ok;
}

static qe_dev_ops i2c_ops = {
	QE_NULL,
	QE_NULL,
	QE_NULL,
	i2c_bus_read,
	i2c_bus_write,
	i2c_bus_ioctl
};

/**
 * @brief I2C bus set lock
 * @param[in] bus: I2C bus
 * @param[in] lock: lock reference
 * @param[in] acquire: lock acquire function
 * @param[in] release: lock release function
 */
qe_ret qe_i2c_bus_setlock(qe_i2c_bus *bus, qe_ptr lock,
	qe_lock_acquire acquire, qe_lock_release release)
{
	if (!bus || !lock || !acquire || !release) {
		qe_error("invalid params");
		return qe_err_param;
	}

	bus->lock.lock    = lock;
	bus->lock.acquire = acquire;
	bus->lock.release = release;
	bus->multi_access = 1;
	return qe_ok;
}

qe_ret qe_i2c_bus_register(qe_i2c_bus *bus, const char *name, qe_i2c_bus_ops *ops, void *priv)
{
	qe_dev *dev;

	qe_assert(bus != QE_NULL);
	qe_assert(name != QE_NULL);

	if (bus->timeout == 0) bus->timeout = 100;

	dev = &(bus->parent);
	dev->priv = priv;
	dev->type = QE_DEV_I2CBUS;
	dev->ops = &i2c_ops;

	bus->ops = ops;
	bus->priv = priv;

	return qe_dev_register(dev, name, QE_DEV_F_RDWR);
}

qe_i2c_bus *qe_i2c_bus_find(const char *name)
{
	qe_dev *dev;
	qe_i2c_bus *bus;

	qe_assert(name);

	dev = qe_dev_find(name);
	if (!dev || (dev->type != QE_DEV_I2CBUS)) {
		qe_error("i2c bus %s not exist", name);
		return QE_NULL;
	}

	bus = (qe_i2c_bus *)dev;

	return bus;
}

qe_int qe_i2c_transfer(qe_i2c_bus *bus, qe_i2c_message *msgs, qe_uint num)
{
	if (!bus) {
		qe_error("bus null");
		return -(qe_err_param);
	}

	if (!bus->ops->master_xfer) {
		qe_error("bus operation has no master xfer");
		return -(qe_err_notsupport);
	}

	return bus->ops->master_xfer(bus, msgs, num);
}

/**
 * @brief I2C transfer with lock
 * @param[in] bus: I2C bus
 * @param[in] msgs: transfer messages
 * @param[in] num: transfer messages number
 * @param[in] wait: lock wait timeout
 * @return >0:success <:error
 */
qe_int qe_i2c_transfer_wait(qe_i2c_bus *bus, qe_i2c_message msgs[], 
	qe_u32 num, qe_uint wait)
{
	qe_int n;
	qe_ret ret;

	if (!bus || !num) {
		qe_error("invalid params");
		return -(qe_err_param);
	}

	if (!bus->lock.lock) {
		qe_error("i2c bus lock not support");
		return -(qe_err_notsupport);
	}

	if (!bus->ops->master_xfer) {
		qe_error("i2c bus master xfer operation not supported");
		return -(qe_err_notsupport);
	}

	ret = bus->lock.acquire(bus->lock.lock, wait);
	if (ret != qe_ok) {
		qe_error("i2c bus lock error:%d", ret);
		return -(ret);
	}
	n = bus->ops->master_xfer(bus, msgs, num);
	bus->lock.release(bus->lock.lock);
	return n;
}

qe_ret qe_i2c_control(qe_i2c_bus *bus, qe_u32 cmd,
    qe_u32 arg)
{
	if (bus->ops->ioctl) {
		return bus->ops->ioctl(bus, cmd, arg);
	} else {
		qe_error("i2c bus operation not supported");
		return 0;
	}
}

qe_int qe_i2c_master_send(qe_i2c_bus *bus, qe_u16 addr,
	qe_u16 flags, const qe_u8 *buf, qe_u32 count)
{
	qe_i2c_message message;

	message.addr  = addr;
	message.flags = flags;
	message.len   = count;
	message.buf   = (qe_u8 *)buf;

	return qe_i2c_transfer(bus, &message, 1);
}

qe_int qe_i2c_master_send_wait(qe_i2c_bus *bus, qe_u16 addr,
	qe_u16 flags, const qe_u8 *buf, qe_u32 count, qe_uint wait)
{
	qe_i2c_message message;

	message.addr  = addr;
	message.flags = flags;
	message.len   = count;
	message.buf   = (qe_u8 *)buf;

	return qe_i2c_transfer_wait(bus, &message, 1, wait);
}

qe_int qe_i2c_master_recv(qe_i2c_bus *bus, qe_u16 addr,
    qe_u16 flags, qe_u8 *buf, qe_u32 count)
{
    qe_i2c_message message;
	
    qe_assert(bus != QE_NULL);

    message.addr   = addr;
    message.flags  = flags | QE_I2C_RD;
    message.len    = count;
    message.buf    = buf;

    return qe_i2c_transfer(bus, &message, 1);
}

qe_int qe_i2c_master_recv_wait(qe_i2c_bus *bus, qe_u16 addr,
    qe_u16 flags, qe_u8 *buf, qe_u32 count, qe_uint wait)
{
    qe_i2c_message message;
	
    qe_assert(bus != QE_NULL);

    message.addr   = addr;
    message.flags  = flags | QE_I2C_RD;
    message.len    = count;
    message.buf    = buf;

    return qe_i2c_transfer_wait(bus, &message, 1, wait);
}